package com.collabscm.sample;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.WeakHashMap;


/**
 * @author Shisong Yang
 * 
 * A Thread Session is utility tool that pass common parameters within one 
 * thread and within one very small piece of time slice. a software 
 * component at an entry point create a new thread session and all the 
 * other components get access to the session. 
 * 
 * This class is only a sample. You can use it in your application, but 
 * more likely, you should overwrite or wrap it and make you own 
 * Thread Session class.
 * 
 * One use case scenario is that a user logs into your application through 
 * the presentation layer, and your business layer wants to know who the 
 * current user is. For some reasons, you do not want to pass a User parameter
 * to all the components of business layer.  At an entry point, you can create 
 * a User instance, and a Thread Session instance, and put the user into the 
 * Thread Session instance. So any other method running in the same thread 
 * can get the user instance from the Thread Session.
 * Here are the sample codes: 
 * 
 *     // at entry point
 *     User user = new User(myName, myPassword);
 *     ThreadSession.getNewInstance().put("APP_USER", user);
 *     ....
 *     
 *     // within a method
 *     {
 *         User user =  (User)ThreadSession.getInstance().get("APP_USER");
 *         If (user.isManager()){
 *             ....
 *         } else {
 *             ....
 *         }
 *     }
 *     .....
 * 
 */
public final class ThreadSession extends HashMap{
    private static WeakHashMap weakMap = new WeakHashMap();
    
    
    private boolean newInstance = true;
    private boolean expired = false;
    private WeakReference thread = null;
    private long id;
    
    private ThreadSession(Thread t){
        thread = new WeakReference(t);
    }
    
    /**
     * Every ThreadSession instance is created by the static method
     * {@link #getInstance()} {@link #getNewInstance()}. This method
     * test whether the returned method is a new instance or a existing
     * one.
     *  
     * @return Return true if the session is new.
     */
    public boolean isNew(){
        return newInstance;
    }
    
    /**
     * A Thread can only have one active ThreadSession. If {@link #getNewInstance()}
     * has not been called, the owner thread will always get the same session 
     * instance, and this instance. but once the owner thread called the 
     * {@link #getNewInstance()} method, a new session will be created, and the old one
     * will be expired.
     * 
     * A ThreadSession is expired when a new ThreadSession instance is created
     * for the owner thread or the owner thread is dead.
     * 
     * The ThreadSession class is designed to use by the owner thread only.
     * in case you passed it to a nother thread, you can use it to test whether
     * this session is expired. 
     * 
     * @return 
     */
    public boolean isExpired(){
        return expired || !isAlive();
    }
    
    /**
     * Test whether the <code>java.lang.Thread</code> to whom this session belongs
     * is still alive.
     * 
     * The ThreadSession class is designed to use by the owner thread only.
     * in case you passed it to a nother thread, you can use it to test whether
     * the owner thread is alive. 
     * 
     * @return Return true if the owner thread is till alive.
     */
    public boolean isAlive(){
        Thread t = (Thread)thread.get();
        return t == null ? false : t.isAlive();
    }

    /**
     * Return the current session. If there is no session, 
     * creats a new one and return it.
     * 
     * @return
     */
    public static synchronized ThreadSession getInstance(){
        Thread current = Thread.currentThread();
        ThreadSession session = (ThreadSession)weakMap.get(current);
        
        if (session == null){
            session = new ThreadSession(current);
            weakMap.put(current, session);
        } else {
            session.newInstance = false;
        }
        
        return session;
    }
    
    /**
     * Create an new session and return it. If there is an old session, 
     * expire the old one.
     * 
     * @return
     */
    protected static synchronized ThreadSession getNewInstance(){
        Thread current = Thread.currentThread();
        ThreadSession session = (ThreadSession)weakMap.get(current);
        
        if (session != null){
            session.expired = true;
        } 
        
        session = new ThreadSession(current);
        weakMap.put(current, session);
        
        return session;
    }
}
